#pragma once
#include <iostream> 
#include <string>
#include <fstream>
#include <cstdarg>
#include <sys/stat.h>
#include <sys/types.h>
#include <ctime>
#include <unistd.h>
#include <mutex>
#include <condition_variable>
class Sem{
    sem_t _sem;

    public:
    Sem( int Soruce_InitNums =0,int process_share = 0)
    {
        sem_init(&_sem,process_share,Soruce_InitNums);
    }
    //放入资源
    void Push()
    {
        sem_post(&_sem);
    }
    //拿走资源
    void Get()
    {
        sem_wait(&_sem);
    }
    ~Sem()
    {
        sem_destroy(&_sem);
    }
    //禁用复制重载和拷贝
    Sem(Sem&) = delete;
    Sem& operator=(Sem&) = delete;
};
class Log{
    
    

    std::string LevelToString(int level)
    {
        std::string strLevel;

        switch (level)
        {
            case MsgLevel::Debug:
                strLevel = "Debug";
                break;
            case MsgLevel::Info:
                strLevel = "Info";
                break;
            case MsgLevel::Warning: // 注意这里的拼写纠正
                strLevel = "Warning";
                break;
            case MsgLevel::Error:
                strLevel = "Error";
                break;
            case MsgLevel::Fatal:
                strLevel = "Fatal";
                break;
            default:
                // 如果 level 不属于上述任何一个枚举值，可以提供一个默认值或者抛出异常等
                strLevel = "Unknown";
                break;
        }

        return strLevel;
    }
    public:
        enum MsgLevel
        {
            Debug = 0,
            Info,
            Warning,
            Error  =11,
            Fatal = 12
        };
        enum PrintTo{
            LogNormol   = 0,
            Screen = 10,
            LogErorr =11,
            LogFatal = 12,
            All,
        };
        Log(int file_style = PrintTo ::Screen,const std::string& filename = "");
        ~Log();

    std:: string GetNowTime()
    {
        //timedatectl   # 查询时区
        //sudo timedatectl set-timezone Asia/Shanghai  # 修改时区
        // printf("[Level:%s] [%s]  {%s}\n", LevelToString(level).c_str(), buffer,std::to_string(currtime).c_str());
        time_t CurTim;
        time(&CurTim);
        struct tm *currtime = localtime(&CurTim);//注意，这里获取的时系统中的Localtime，由于linux大多数默认时区是美国那边的，故而会有时差
        char buffer[128];
        snprintf(buffer,sizeof(buffer),"time::%d:%d  <> %d-%d-%d",currtime->tm_hour,currtime->tm_min,currtime->tm_mday,currtime->tm_mon+1,currtime->tm_year+1900);
        return buffer;
    }
    void operator()(int level, const char *format, ...){
        char buffer[1024];
        va_list args;
        va_start(args, format);
        //使得args指向可变参数部分

        vsnprintf(buffer, sizeof(buffer), format, args);
        va_end(args);
        
        char buffer2[2048];
        snprintf(buffer2,sizeof(buffer2),"[Level:%s][PID:%d] [%s]  {%s}\n", LevelToString(level).c_str(), getpid(),buffer,GetNowTime().c_str());
        WriteLog(buffer2);
    }
    void WriteToFile(const std::string &filename,const std::string & Msg)
    {
        std::ofstream ofs(_filename + filename,std::ios::app | std::ios::ate);
        if(!ofs.is_open())
        {
            return ;
        }
        {
            _static_Sem.Get();
            ofs<<Msg;
            _static_Sem.Push();
        }
        ofs.close();

    }
    void WriteLog(const std::string& Msg)
    {
        switch (_file_style)
        {
        case PrintTo::Screen:
            {
                _static_Sem.Get();
                std::cout<<Msg;
                _static_Sem.Push();

            }
            break;


        //可以在此处修改，让日志名与等级挂钩
        case PrintTo::LogNormol:
                WriteToFile("NormolLog.txt",Msg);
            break;
        case PrintTo::LogFatal:
            WriteToFile("FatalLog.txt",Msg);
        break;
        case PrintTo::LogErorr:
                WriteToFile("ErrorLog.txt",Msg);
            break;
        case PrintTo::All:
                WriteToFile("Alllog.txt",Msg);
        default:
            break;
        }
    }
    void Style(int Style = PrintTo::Screen)
    {
        _file_style = Style;
    }

    private:
    int _file_style = PrintTo::Screen;
    std::string _filename;
    
    //其实每一种日志文件对应一个信号量，效率理应才最搞，这里就不多实现
    static Sem _static_Sem;
};

//打印语言的缓冲区一般都只有一个，所以我们就设置只有一个初始值即可
Sem Log::_static_Sem(1);

Log::Log(int file_style,const std::string& filename)
:_file_style(file_style)
{
    mkdir("Log",0775);
    if(file_style != PrintTo::Screen)
    {
        _filename = "./Log/"+filename;
    }   
}

Log::~Log()
{

}
